package com.heima.lock.controller;

import com.heima.lock.mapper.MyLockMapper;
import com.heima.lock.pojos.MyLock;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.concurrent.TimeUnit;

/**
 * @author jack
 * @data 2023 9:15
 */
@RestController
@RequestMapping("/api/v1/stock")
public class StockController {
    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    @Qualifier("myLockMapper")
    private MyLockMapper myLockMapper;

    @Autowired
    private RedissonClient redissonClient;

    /**
     * synchronized解决不了分布式下的多线程数据安全问题
     *
     * @return
     */
    @GetMapping("/order")
    public String order() {
        //1. 先获取当前库存数据，判断是否还够
        synchronized ("abc") {      // synchronized: 只能锁住单个JVM内的线程
            String stockStr = stringRedisTemplate.opsForValue().get("stock");
            if (!StringUtils.isEmpty(stockStr)) {
                Integer stock = Integer.valueOf(stockStr);
                if (stock > 0) {
                    //2. 够：就下单成功，同时扣减库存
                    stock--;
                    System.out.println(System.currentTimeMillis() + "下单成功，库存剩余：" + stock);
                    stringRedisTemplate.opsForValue().set("stock", String.valueOf(stock));
                    return "order success!!";
                } else {
                    System.out.println(System.currentTimeMillis() + "下单失败，库存不足：" + stock);
                    return "order error!!";
                }
            } else {
                //3. 不够：就下单失败
                System.out.println(System.currentTimeMillis() + "下单失败，库存不足!");
                return "order error!!";
            }
        }
    }


    /**
     * 基于mysql实现分布式锁
     * 非阻塞锁
     *
     * @return
     */
    @GetMapping("/order1")
    public String order1() {

        try {
            //形成多JVM内线程的互斥效果  -- 分布式锁核心: 就是确保多个JVM访问相同的临界资源，在临界资源上形成互斥
            MyLock lock = MyLock.builder()
                    .threadName(Thread.currentThread().getName())
                    .lockName("stockLock")
                    .build();
            myLockMapper.insert(lock);

            //1. 先获取当前库存数据，判断是否还够
            String stockStr = stringRedisTemplate.opsForValue().get("stock");
            if (!StringUtils.isEmpty(stockStr)) {
                Integer stock = Integer.valueOf(stockStr);
                if (stock > 0) {
                    //2. 够：就下单成功，同时扣减库存
                    stock--;
                    System.out.println(System.currentTimeMillis() + "下单成功，库存剩余：" + stock);
                    stringRedisTemplate.opsForValue().set("stock", String.valueOf(stock));


                    //释放锁
                    myLockMapper.deleteById(lock.getId());

                    return "order success!!";
                } else {
                    System.out.println(System.currentTimeMillis() + "下单失败，库存不足：" + stock);
                    return "order error!!";
                }
            } else {
                //3. 不够：就下单失败
                System.out.println(System.currentTimeMillis() + "下单失败，库存不足!");
                return "order error!!";
            }
        } catch (Exception e) {
            System.out.println(System.currentTimeMillis() + "下单失败，服务器正忙!");
            return "order error!!";
        }

    }

    /**
     * 基于redis实现分布式锁
     * 非阻塞锁
     *
     * @return
     */
    @GetMapping("/order2")
    public String order2() {
        //形成多JVM内线程的互斥效果  -- 分布式锁核心: 就是确保多个JVM访问相同的临界资源，在临界资源上形成互斥
        Boolean isLock = stringRedisTemplate.opsForValue().setIfAbsent("stockLock", String.valueOf(Thread.currentThread().getId()));
        try {
            if (isLock) {
                //1. 先获取当前库存数据，判断是否还够
                String stockStr = stringRedisTemplate.opsForValue().get("stock");
                if (!StringUtils.isEmpty(stockStr)) {
                    Integer stock = Integer.valueOf(stockStr);
                    if (stock > 0) {
                        //2. 够：就下单成功，同时扣减库存
                        stock--;
                        System.out.println(System.currentTimeMillis() + "下单成功，库存剩余：" + stock);
                        stringRedisTemplate.opsForValue().set("stock", String.valueOf(stock));
                        return "order success!!";
                    } else {
                        System.out.println(System.currentTimeMillis() + "下单失败，库存不足：" + stock);
                    }
                } else {
                    //3. 不够：就下单失败
                    System.out.println(System.currentTimeMillis() + "下单失败，库存不足!");
                }
            } else {
                System.out.println(System.currentTimeMillis() + "下单失败，服务器正忙!");
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //释放锁: 只能自己释放自己的锁
            String threadId = stringRedisTemplate.opsForValue().get("stockLock");
            if (String.valueOf(Thread.currentThread().getId()).equals(threadId)) {
                stringRedisTemplate.delete("stockLock");
            }
        }

        return "order error!!";
    }


    /**
     * 基于redisson实现分布式锁
     * 非阻塞锁
     *
     * @return
     */
    @GetMapping("/order3")
    public String order3() {
        //形成多JVM内线程的互斥效果  -- 分布式锁核心: 就是确保多个JVM访问相同的临界资源，在临界资源上形成互斥
        RLock rLock = redissonClient.getLock("stockLock");      //注意：这里仅仅只是创建了一个RLock对象
        //rLock.lock();           //阻塞锁实现
        boolean isLock = rLock.tryLock();       //非阻塞锁实现
        try {
            if (isLock) {
                //1. 先获取当前库存数据，判断是否还够
                String stockStr = stringRedisTemplate.opsForValue().get("stock");
                if (!StringUtils.isEmpty(stockStr)) {
                    Integer stock = Integer.valueOf(stockStr);
                    if (stock > 0) {
                        //2. 够：就下单成功，同时扣减库存
                        stock--;
                        System.out.println(System.currentTimeMillis() + "下单成功，库存剩余：" + stock);
                        stringRedisTemplate.opsForValue().set("stock", String.valueOf(stock));
                        return "order success!!";
                    } else {
                        System.out.println(System.currentTimeMillis() + "下单失败，库存不足：" + stock);
                    }
                } else {
                    //3. 不够：就下单失败
                    System.out.println(System.currentTimeMillis() + "下单失败，库存不足!");
                }
            } else {
                System.out.println(System.currentTimeMillis() + "下单失败，服务器正忙!");
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //释放锁: 只能自己释放自己的锁
            rLock.unlock();
        }

        return "order error!!";
    }

    /**
     * 基于redisson实现分布式锁
     * 阻塞锁
     *
     * @return
     */
    @GetMapping("/order4")
    public String order4() {
        //形成多JVM内线程的互斥效果  -- 分布式锁核心: 就是确保多个JVM访问相同的临界资源，在临界资源上形成互斥
        RLock rLock = redissonClient.getLock("stockLock");      //注意：这里仅仅只是创建了一个RLock对象
        rLock.lock();           //阻塞锁实现
        try {
            //1. 先获取当前库存数据，判断是否还够
            String stockStr = stringRedisTemplate.opsForValue().get("stock");
            if (!StringUtils.isEmpty(stockStr)) {
                Integer stock = Integer.valueOf(stockStr);
                if (stock > 0) {
                    //2. 够：就下单成功，同时扣减库存
                    stock--;
                    System.out.println(System.currentTimeMillis() + "下单成功，库存剩余：" + stock);
                    stringRedisTemplate.opsForValue().set("stock", String.valueOf(stock));
                    return "order success!!";
                } else {
                    System.out.println(System.currentTimeMillis() + "下单失败，库存不足：" + stock);
                }
            } else {
                //3. 不够：就下单失败
                System.out.println(System.currentTimeMillis() + "下单失败，库存不足!");
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //释放锁: 只能自己释放自己的锁
            rLock.unlock();
        }
        return "order error!!";
    }

}
